package au.com.betaversion;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import au.com.betaversion.aws.PhotoStorage;
import au.com.betaversion.domain.Airport;
import au.com.betaversion.domain.Photo;
import au.com.betaversion.domain.View;
import au.com.betaversion.repository.AircraftRepository;
import au.com.betaversion.repository.AirportRepository;
import au.com.betaversion.repository.PhotoRepository;
import au.com.betaversion.repository.RegisteredUserRepository;
import au.com.betaversion.repository.ViewRepository;
import au.com.betaversion.service.PhotoService;

import com.mortennobel.imagescaling.ResampleOp;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final int THUMBNAIL = 250;

	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@Autowired
	private PhotoRepository photoRepository;
	
	@Autowired
	private PhotoService photoService;
	
	@Autowired
	private RegisteredUserRepository userRepository;
	
	@Autowired
	private AircraftRepository aircraftRepository;
	
	@Autowired
	private AirportRepository airportRepository;
	
	@Autowired
	private ViewRepository viewRepository;
	
	@Autowired
	@Qualifier("photoStorageS3")
	private PhotoStorage photoStorage;
	
	@RequestMapping(value ="/", method = RequestMethod.GET)
	public String display(Model model) {
		List<Photo> files = (List<Photo>) photoRepository.findAll();
		
		Collections.sort(files);
		
		model.addAttribute("files", files);
		model.addAttribute("fileslice", files.subList(0, 3));
		model.addAttribute("numberOfPhotos", photoRepository.count());
		model.addAttribute("numberOfAircrafts", aircraftRepository.count());
		model.addAttribute("numberOfAirports", airportRepository.count());

		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if(authentication instanceof AnonymousAuthenticationToken) {
			model.addAttribute("message", "Please log in to upload your own photos");
			return "home";
		}
		return "home";
	}

	@RequestMapping(value="/gallery/{username}")
	public String displayByUsername(@PathVariable("username") String username, Model model) {
		List<Photo> photos = (List<Photo>) photoRepository.findByUsername(username);
		
		if(photos.size() == 0) {
			model.addAttribute("error", "No photos to display");
			return "gallery";
		}
		
		Collections.sort(photos);
		
		model.addAttribute("files", photos);
		return "gallery";
	}
	
	@RequestMapping(value ="/upload", method = RequestMethod.GET)
	public String uploadFileForm(Model model) {
		model.addAttribute("airports", airportRepository.findAll());
		model.addAttribute("uploadForm", new Photo());
		return "upload";
	}
	
	@RequestMapping(value ="/upload", method = RequestMethod.POST)
	public String uploadFile(@ModelAttribute("uploadForm") Photo photo, 
			@RequestParam("file") MultipartFile file, Model model, RedirectAttributes attributes) {
		if(!file.isEmpty()) {
			logger.info("File content: " + file.getContentType());
			logger.info("File name: " + file.getOriginalFilename());
			if(!file.isEmpty()) {
				try {
					photo.setOriginalFileName(file.getOriginalFilename());
					photo.setContentType(file.getContentType());
					//photo.setPhoto(file.getBytes());
					
					Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
					UserDetails userDetails = (UserDetails)authentication.getPrincipal();
					photo.setRegisteredUser(userRepository.findOne(userDetails.getUsername()));
					
					if(aircraftRepository.findOne(photo.getAircraft().getMark()) == null) {
						aircraftRepository.save(photo.getAircraft());
					}
					
					InputStream inputStream = new ByteArrayInputStream(file.getBytes());
					BufferedImage originalImage = ImageIO.read(inputStream);
					
					int width = originalImage.getWidth();
					int height = originalImage.getHeight();
					
					double ratio = (double)width/height; 
					
					int newWidth = (int) THUMBNAIL;
					int newHeight = (int) (THUMBNAIL / ratio);
					
					ResampleOp resampleOp = new ResampleOp(newWidth, newHeight);
					BufferedImage bufferedImage = resampleOp.filter(originalImage, null);

					ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
					ImageIO.write(bufferedImage, "jpg", outputStream);
					outputStream.flush();
					photo.setThumbnail(outputStream.toByteArray());
					outputStream.close();

					photo.setDateUploaded(new Date());

					File s3File = new File("/tmp/temp_s3");
					file.transferTo(s3File);
					
					String uuid  = UUID.randomUUID().toString();
					
					String fileName = uuid + "." + FilenameUtils.getExtension(file.getOriginalFilename());
					
					if(photoStorage.uploadFile(fileName, s3File)) {
						photo.setStorageId(fileName);
						photoRepository.save(photo);						
						attributes.addFlashAttribute("message", "Photo uploaded successfully");
					} else {
						attributes.addFlashAttribute("error", "Unexpected error. Please try again in few minutes");
					}
				} catch (IOException e) {
					attributes.addFlashAttribute("error", e.getCause());
				} finally {
				}
			}
		}
		return "redirect:/";
	}

	@RequestMapping(value = "/photo/thumbnail/{id}", method = RequestMethod.GET)
	public void getFileThumbnail(@PathVariable("id") String id, HttpServletResponse response) {
		try {
			Photo photo = photoRepository.findOne(id);
			
			response.setContentType(photo.getContentType());
			response.getOutputStream().write(photo.getThumbnail());
			response.getOutputStream().flush();
			response.getOutputStream().close();
		} catch(IOException e) {
			e.printStackTrace();
		}
		
	}
	
	@RequestMapping(value = "/photo/display/{id}", method = RequestMethod.GET)
	public String displayPhoto(@PathVariable("id") String id, Model model, HttpServletRequest request) {
		model.addAttribute("photo", photoRepository.findOne(id));
		model.addAttribute("id", id);
		
		String ip = request.getHeader("X-Cluster-Client-IP");
		if(ip == null)
			ip = request.getRemoteAddr();
		
		View view = new View();
		view.setPhotoId(id);
		view.setIp(ip);
		view.setDate(new Date());
		viewRepository.save(view);
		
		photoService.increaseView(id);
		
		return "photo-display";
	}
	
	@RequestMapping(value = "/file/delete/{id}", method = RequestMethod.GET)
	public String deleteFile(@PathVariable("id") String id, RedirectAttributes attributes) {
		Photo photo = photoRepository.findOne(id);
		if(photoStorage.deleteFile(photo.getStorageId())) {
			photoRepository.delete(id);
			attributes.addFlashAttribute("message", "Record deleted");
		}
		return "redirect:/";
	}
	
	@RequestMapping(value = "/file/edit/{id}", method = RequestMethod.GET)
	public String editFile(@PathVariable("id") String id, Model model) {
		model.addAttribute("uploadForm", photoRepository.findOne(id));
		return "edit";
	}
	
	@RequestMapping(value = "/file/edit/{id}", method = RequestMethod.POST)
	public String editFilePost(@PathVariable("id") String id, 
			@ModelAttribute("uploadForm") Photo photo, Model model) {
		Photo photoFromDb = photoRepository.findOne(id);
		photoFromDb.setComment(photo.getComment());
		photoFromDb.setDateTaken(photo.getDateTaken());
		photoFromDb.getAircraft().setMark(photo.getAircraft().getMark());
		if(photoFromDb.getAirport() == null)
			photoFromDb.setAirport(new Airport(photo.getAirport().getIata()));
		else
			photoFromDb.getAirport().setIata(photo.getAirport().getIata());
		photoRepository.save(photoFromDb);
		return "redirect:/";
	}
	
	@RequestMapping(value = "/file/reset/{id}", method = RequestMethod.GET)
	public String resetViews(@PathVariable("id") String id, Model model, RedirectAttributes attributes) {
		Photo photo = photoRepository.findOne(id);
		photo.setViews(0);
		photoRepository.save(photo);
		
		List<View> views = viewRepository.findAllByPhoto(id);
		viewRepository.delete(views);
		
		attributes.addFlashAttribute("message", "Views reset for photo id " + id);
		return "redirect:/";
	}
	
	@RequestMapping(value = "/login")
	public String loginPage() {
		return "login";
	}
	
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
		CustomDateEditor dateEditor = new CustomDateEditor(dateFormat, false);
		binder.registerCustomEditor(java.util.Date.class, dateEditor);
	}

}
